GeeKee CeeBee

Welcome to GeeKee CeeBee's Page: House of Mechatronics Projects & Lessons.
Contact Email: Ceebee1108@gmail.com
Follow me on Youtube
__________________________________________________________________________________________________________________________________

TETRIS with ARDUINO & MAX7219

Step by step practical guide to building Tetris game using LED matrix and Arduino. Build a program flowchart to carve out game logic. Implement Joystick for game controls. Modify SPI library to suit our project needs. Demonstrate the use of 8x8 LED MATRIX.
Disclaimer: The code and other information on this project are provided on "AS IS" basis with no warranty. Please follow best practices and assess your own potential risks for this project.

Components List

Arduino Uno (Affiliate Link)
MAX7219 8x8 Dot Matrix LED(Affiliate Link)
Joystick Breakout Mouduke(Affiliate Link)
Jumper Wires and Breadboard (Affiliate Link)

Step-by-step video guide

In this video guide, you will learn to create simple Tetris game by learning to control MAX7219 8x8 LED Matrix over Serial Peripheral Interface (SPI). Arduino Uno is used to form Tetris blocks on the Matrix , and Joystick breakout module is used to control the movements of the blocks. Video covers using and modifying SPI libraries, program and game logic implementation and live demo.


Wiring Diagram

Below is the wiring diagram for the components used in this project. LED matrix SPI wiring only has 3 wires, CS (chip select), DIN (Data in) and CLK (clock). Since LED matrix is not sending any data out, it does not have 4th wire (Data out) found in a typical SPI communication. Use pin 10, 11, and 12 for SPI communication. Analog pins A0 and A1 are used for joystick X and Y direction control, while pin2 is used for switch press detection on a joystick. Make sure there's a common ground (not earth ground) between the LED matrix, arduino and joystick.

__________________________________________________________________________________________________________________________________


PROGRAM FLOWCHART

Below is the program flowchart for this project. It is generally helpful to create flowchart for how your algorithm and game logic is designed. It gets really difficult to keep track of these when the project gets larger in size. Hopefully, this flowchart will be helpful in better understanding the game logic used in this project.


__________________________________________________________________________________________________________________________________


Adding custom code to existing LedControl library by Wayoda

Prerequisites: Add below code to LedControl.cpp
    
// To get particular LED status*****************************************
int LedControl::getstatus(int addr, int row, int column){
switch (column) {
	case 0:
		column = 7;
		break;
	case 1:
		column = 6;
		break;
	case 2:
		column = 5;
		break;
	case 3:
		column = 4;
		break;
	case 4:
		column = 3;
		break;
	case 5:
		column = 2;
		break;
	case 6:
		column = 1;
		break;
	case 7:
		column = 0;
		break;
}
int new_col=bitRead(status[row],column);
if (new_col == 1)
return new_col;
else if (new_col == 0)
return new_col;
}


// To check & perform line break task ***************************************
void LedControl::line_break(){
for(int k=0;k<6;k++){
if(status[0] == 255 ){
status[7]=0;
for (int i=0; i<8; i++){
status[i] =status[i+1];
spiTransfer(0, i+1,status[i]);
      }
     }
else{}
  }
}


// To check & perform Gameover task *****************************************
void LedControl::gameover(){
for (int k=1; k<9; k++){
int a=0;
int p=0;
for (int i=0; i<8; i++){
a=getstatus(0,i,k);
p=p+a;
}
Serial.println(p);
 if(p == 8 )
{
k=10;
Serial.println("   GameOver   ");
shutdown(0,true);
delay(200);
shutdown(0,false);
delay(200);
shutdown(0,true);
delay(200);
shutdown(0,false);
delay(200);
shutdown(0,true);
delay(10000);
}}
}
    
  


Prerequisites: Add below code to LedControl.h under class LedControl Public member
    

      int getstatus(int addr, int row, int column);
      void line_break();
      void gameover();
    
  








Arduino Code

TETRIS using ARDUINO & MAX7219 8x8 LED MATRIX
    

//TETRIS by GEEKEE CEEBEE
/*    THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
*/


#include < LedControl.h >

//We always have to include the library
#include "LedControl.h"


//***************Global Variables and Definitions*****************
LedControl lc = LedControl(12, 11, 10, 4);
const int SW_pin = 2;         // digital pin connected to SW
const int X_pin = A0;         // analog pin connected to VRx
const int Y_pin = A1;
float r = 7.0;               // row input
int c = 3;                   // column input
int add = 0;                 // address input
int add_prev = 1000;
int r_prev = 1000;
int c_prev = 1000;
int ro = 0;                  // to set block orientation
int ro_prev;
int c2;
float r2;
int check1, check2, check3, check4;
int left_check1, left_check2, left_check3, left_check4;
int right_check1, right_check2, right_check3, right_check4;
int block = 0;

//*******************Right Translate Functions*********************
int Right_translate( int c1, boolean state1) {
  int X = analogRead(X_pin);
  if (state1 == true && X < 200) {
    return c--;
  }
  else if (state1 == false && X < 200) {
    return c;
  }
}


//*******************Left Translate Functions******************
int Left_translate( int c1, boolean state1) {
  int X = analogRead(X_pin);
  if (state1 == true) {
    if (X > 800) {
      return  c++;
    }
  }
  else if (state1 == false) {
    if (X > 800) {
      return   c;
    }
  }
}


//*******************Down Translate Functions***************
float Down_translate( float r1, boolean state1) {
  int Y = analogRead(Y_pin);
  //Serial.print(" Y_pin: ");
  //        Serial.print(Y);
  if (state1 == true ) {
    if (Y > 800) {
      return r--;
    }
  }
  else if (state1 == false) {
    if (Y > 800) {
      return   r;
    }
  }
}






//************************************  I BLOCK FUNCTION  ***********
void I_block(int add, float r, int c, int ro) {

  switch (ro) {

    case 0:                                   // Horizontal Orientation
      if (ro_prev == 1) {
        lc.setLed(add_prev, r_prev, c_prev, false);
        lc.setLed(add_prev, r_prev - 1, c_prev, false);
        lc.setLed(add_prev, r_prev - 2, c_prev, false);
        lc.setLed(add_prev, r_prev - 3, c_prev, false);
      }


      lc.setLed(add_prev, r_prev, c_prev, false);
      lc.setLed(add_prev, r_prev, c_prev - 1, false);
      lc.setLed(add_prev, r_prev, c_prev - 2, false);
      lc.setLed(add_prev, r_prev, c_prev - 3, false);

      lc.setLed(add, r, c, true);
      lc.setLed(add, r, c - 1, true);
      lc.setLed(add, r, c - 2, true);
      lc.setLed(add, r, c - 3, true);

      check1 = lc.getstatus(add, r - 1, c);
      check2 = lc.getstatus(add, r - 1, c - 1);
      check3 = lc.getstatus(add, r - 1, c - 2);
      check4 = lc.getstatus(add, r - 1, c - 3);
      left_check1 = lc.getstatus(add, r , c + 1);
      right_check1 = lc.getstatus(add, r , c - 4);

      add_prev = add;
      r_prev = r;
      c_prev = c;
      ro_prev = ro;


      break;

    case 1:                                         // Vertical Orientation
      if (ro_prev == 0) {
        lc.setLed(add_prev, r_prev, c_prev, false);
        lc.setLed(add_prev, r_prev, c_prev - 1, false);
        lc.setLed(add_prev, r_prev, c_prev - 2, false);
        lc.setLed(add_prev, r_prev, c_prev - 3, false);
      }

      lc.setLed(add_prev, r_prev, c_prev, false);
      lc.setLed(add_prev, r_prev - 1, c_prev, false);
      lc.setLed(add_prev, r_prev - 2, c_prev, false);
      lc.setLed(add_prev, r_prev - 3, c_prev, false);

      lc.setLed(add, r, c, true);
      lc.setLed(add, r - 1, c, true);
      lc.setLed(add, r - 2, c, true);
      lc.setLed(add, r - 3, c, true);

      check1 = lc.getstatus(add, r - 4, c);
      left_check1 = lc.getstatus(add, r , c + 1);

      right_check1 = lc.getstatus(add, r , c - 1);


      add_prev = add;
      r_prev = r;
      c_prev = c;
      ro_prev = ro;

      break;

  }

}

//************************************  S BLOCK FUNCTION  ********************
void S_block(int add, float r, int c, int ro) {

  switch (ro) {

    case 0:                                          // Horizontal Orientation
      if (ro_prev == 1) {

        lc.setLed(add_prev, r_prev, c_prev, false);
        lc.setLed(add_prev, r_prev, c_prev - 1, false);
        lc.setLed(add_prev, r_prev - 1, c_prev - 1, false);
        lc.setLed(add_prev, r_prev + 1, c_prev, false);

      }
      lc.setLed(add_prev, r_prev, c_prev, false);
      lc.setLed(add_prev, r_prev, c_prev - 1, false);
      lc.setLed(add_prev, r_prev - 1, c_prev, false);
      lc.setLed(add_prev, r_prev - 1, c_prev + 1, false);


      lc.setLed(add, r, c, true);
      lc.setLed(add, r, c - 1, true);
      lc.setLed(add, r - 1, c, true);
      lc.setLed(add, r - 1, c + 1, true);

      check1 = lc.getstatus(add, r - 2, c);
      check2 = lc.getstatus(add, r - 2, c + 1);
      check3 = lc.getstatus(add, r - 1, c - 1);
      left_check1 = lc.getstatus(add, r , c + 2);
      right_check1 = lc.getstatus(add, r , c - 2);


      add_prev = add;
      r_prev = r;
      c_prev = c;
      ro_prev = ro;

      break;

    case 1:                                            // Vertical Orientation
      if (ro_prev == 0) {
        lc.setLed(add_prev, r_prev, c_prev, false);
        lc.setLed(add_prev, r_prev, c_prev - 1, false);
        lc.setLed(add_prev, r_prev - 1, c_prev, false);
        lc.setLed(add_prev, r_prev - 1, c_prev + 1, false);
      }

      lc.setLed(add_prev, r_prev, c_prev, false);
      lc.setLed(add_prev, r_prev, c_prev - 1, false);
      lc.setLed(add_prev, r_prev - 1, c_prev - 1, false);
      lc.setLed(add_prev, r_prev + 1, c_prev, false);

      lc.setLed(add, r, c, true);
      lc.setLed(add, r, c - 1, true);
      lc.setLed(add, r - 1, c - 1, true);
      lc.setLed(add, r + 1, c, true);

      check1 = lc.getstatus(add, r - 1, c);
      check2 = lc.getstatus(add, r - 2, c - 1);
      left_check1 = lc.getstatus(add, r , c + 1);
      right_check1 = lc.getstatus(add, r , c - 2);

      add_prev = add;
      r_prev = r;
      c_prev = c;
      ro_prev = ro;
      break;

  }

}




//*********************************  MAIN SETUP  ***************
void setup() {
  //we have already set the number of devices when we created the LedControl
  int devices = lc.getDeviceCount();
  //lc.line_break();
 // int che = lc.getstatus(add, r, c);
  pinMode(SW_pin, INPUT);
  digitalWrite(SW_pin, HIGH);
  randomSeed(analogRead(0));
  Serial.begin(9600);
  //we have to init all devices in a loop
  for (int address = 0; address < devices; address++) {
    /*The MAX72XX is in power-saving mode on startup*/
    lc.shutdown(address, false);
    /* Set the brightness to a medium values */
    lc.setIntensity(address, 8);
    /* and clear the display */
    lc.clearDisplay(address);
  }

}


//************************  MAIN LOOP FUNCTION  *****************
void loop() {
  block = random(-1, 2);  // Block selection by
                          // randomly picking value between 0 and 1
  r = 8;                  // Starting row for new block
  c = 3;                  // starting column for new block
  add = 0;                 // address is 0 if using only one 8x8 LED module

  switch (block) {

    //****************   I_block Horizontal  *********
    case 1:
      do {
        //  int X = analogRead(X_pin);
        //        int Y = analogRead(Y_pin);
        int Rot = digitalRead(SW_pin);
        if (Rot == LOW) {
          ro++;
          if (ro == 2) {
            ro = 0;
          }
        }


        if (c >= -1 && c <= 8) {

          switch (ro) {
            case 0:
              if (c == 2 || c == 1 || c == 0) {
                c = 3;
              }
              if (c == 8) {
                c = 7;
              }
              if (left_check1 == 1 && right_check1 == 0) {
                c = Right_translate(c, true);
                Left_translate(c, false);
                Down_translate(r, true);
              }
              else if (left_check1 == 0 && right_check1 == 1) {
                c = Right_translate(c, false);
                Left_translate(c, true);
                Down_translate(r, true);
              }
              else if (left_check1 == 1 && right_check1 == 1) {
                c = Right_translate(c, false);
                Left_translate(c, false);
                Down_translate(r, true);
              }


              else if (left_check1 == 0 && right_check1 == 0) {
                c2 = Right_translate(c, true);
                c2 = Left_translate(c, true);
                r2 = Down_translate(r, true);
              }


              break;

            //**********************  I block_Vertical  **************

            case 1:
              if (c == -1) {
                c = 0;
              }
              if (c == 8) {
                c = 7;
              }

              if (left_check1 == 1 && right_check1 == 0) {
                c = Right_translate(c, true);
                Left_translate(c, false);
                Down_translate(r, true);
              }
              else if (left_check1 == 0 && right_check1 == 1) {
                c = Right_translate(c, false);
                Left_translate(c, true);
                Down_translate(r, true);
              }
              else if (left_check1 == 1 && right_check1 == 1) {
                c = Right_translate(c, false);
                Left_translate(c, false);
                Down_translate(r, true);
              }


              else if (left_check1 == 0 && right_check1 == 0) {
                c2 = Right_translate(c, true);
                c2 = Left_translate(c, true);
                r2 = Down_translate(r, true);
              }


              break;


          }

        }
        r = r - 0.125;
        I_block(add, r, c, ro);
        if (check1 == 1 || check2 == 1 || check3 == 1) {
          r = 0.8;
        }
        delay(225);
      } while (r > 0.9 );
      add_prev = 100;
      r_prev = 100;
      c_prev = 100;
      break;

    //*****************S_Block case*****************

    case 0:
      do {

        int Rot = digitalRead(SW_pin);
        if (Rot == LOW) {
          ro++;
          if (ro == 2) {
            ro = 0;
          }
        }


        if (c >= 0 && c <= 7) {

          switch (ro) {
            case 0:
              if ( c == 0) {
                c = 1;
              }
              if (c == 7) {
                c = 6;
              }
                            if (left_check1 == 1 && right_check1 == 0) {
                              c = Right_translate(c, true);
                              Left_translate(c, false);
                              Down_translate(r, true);
                            }
                            else if (left_check1 == 0 && right_check1 == 1) {
                              c = Right_translate(c, false);
                              Left_translate(c, true);
                              Down_translate(r, true);
                            }
                            else if (left_check1 == 1 && right_check1 == 1) {
                              c = Right_translate(c, false);
                              Left_translate(c, false);
                              Down_translate(r, true);
                            }


                            else if (left_check1 == 0 && right_check1 == 0) {
              c2 = Right_translate(c, true);
              c2 = Left_translate(c, true);
              r2 = Down_translate(r, true);
                            }


              break;

            //***************************S block**************

            case 1:
              if (c == -1) {
                c = 0;
              }
              if (c == 8) {
                c = 7;
              }
             if (left_check1 == 1 && right_check1 == 0) {
                              c = Right_translate(c, true);
                              Left_translate(c, false);
                              Down_translate(r, true);
                            }
                            else if (left_check1 == 0 && right_check1 == 1) {
                              c = Right_translate(c, false);
                              Left_translate(c, true);
                              Down_translate(r, true);
                            }
                            else if (left_check1 == 1 && right_check1 == 1) {
                              c = Right_translate(c, false);
                              Left_translate(c, false);
                              Down_translate(r, true);
                            }


                            else if (left_check1 == 0 && right_check1 == 0) {
              c2 = Right_translate(c, true);
              c2 = Left_translate(c, true);
              r2 = Down_translate(r, true);
                            }

              break;


          }



        }
        r = r - 0.125;
        S_block(add, r, c, ro);
        if (check1 == 1 || check2 == 1 || check3 == 1) {
          r = 1.8;
        }
        delay(225);
      } while (r > 1.9 );
      add_prev = 100;
      r_prev = 100;
      c_prev = 100;
      break;


  }



  lc.line_break();  // Line Break function called from LED control libraries
  lc.gameover();    // Game Over function called from LED control libraries


}



    
  
<